import json
import re
import traceback
from typing import Dict
import logging
import os
from logging import handlers


def _logging(**kwargs):
    level = kwargs.pop('level', logging.DEBUG)
    filename = kwargs.pop('filename', 'default.log')
    datefmt = kwargs.pop('datefmt', '%Y-%m-%d %H:%M:%S')
    format = kwargs.pop('format', '[%(asctime)s,%(msecs)d][%(module)s][%(levelname)s] %(lineno)d - %(message)s')
    log = logging.getLogger(filename)
    format_str = logging.Formatter(format, datefmt)

    th = handlers.TimedRotatingFileHandler(filename=filename, when='MIDNIGHT', backupCount=30, encoding="utf-8")
    th.suffix = "%Y%m%d.log"
    th.extMatch = re.compile(r"^\d{4}\d{2}\d{2}(\.\w+)?$", re.ASCII)
    th.setFormatter(format_str)
    th.setLevel(level)

    log.addHandler(th)
    log.setLevel(level)
    return log


root_dir = os.path.dirname(os.path.abspath(__file__))
lib_dir = os.path.join(root_dir, "lib")
logs_dir = os.path.join(root_dir, "logs")

os.makedirs(logs_dir, exist_ok=True)
os.makedirs(lib_dir, exist_ok=True)
logger = _logging(filename="./logs/address.log")


address_file_path = os.path.join(lib_dir, "address.json")
# province_file_path = os.path.join(lib_dir, "province.json")  # 省份
# city_file_path = os.path.join(lib_dir, "city.json")  # 城市
# district_file_path = os.path.join(lib_dir, "district.json")  # 区县
# town_file_path = os.path.join(lib_dir, "town.json")  # 乡镇
# village_file_path = os.path.join(lib_dir, "village.json")  # 村镇


class AddressHandler:
    def __init__(self):
        self.address_datas: Dict[str] = {}
        self.province_dict: Dict[str, str] = {}
        self.city_dict: Dict[str, str] = {}
        self.district_dict: Dict[str, str] = {}
        self.town_dict: Dict[str, str] = {}
        self.village_dict: Dict[str, str] = {}
        self.load_datas()

    def load_datas(self):
        logger.info(f"load address data ...")
        self.address_datas = json.load(open(address_file_path, encoding='utf-8'))
        for province_code in self.address_datas:
            province_info = self.address_datas[province_code]
            province_name = province_info["province_name"]
            self.province_dict[province_code] = province_name  # 获取省份编码映射
            city_datas = province_info["city_datas"]
            for city_code in city_datas:
                city_info = city_datas[city_code]
                city_name = city_info["city_name"]
                self.city_dict[city_code] = city_name  # 获取城市编码映射
                district_datas = city_info["district_datas"]
                for district_code in district_datas:
                    district_info = district_datas[district_code]
                    district_name = district_info["district_name"]
                    self.district_dict[district_code] = district_name  # 获取区县编码映射
                    town_datas = district_info["town_datas"]
                    for town_code in town_datas:
                        town_info = town_datas[town_code]
                        town_name = town_info["town_name"]
                        self.town_dict[town_code] = town_name  # 获取区县编码映射
                        village_datas = town_info["village_datas"]
                        for village_code in village_datas:
                            village_name = village_datas[village_code]
                            self.village_dict[village_code] = village_name

    def get_province_info(self, address: str):
        for code, name in self.province_dict.items():
            tmp_name = str(name).replace("省", "").replace("市", "").replace("自治区", "").replace("维吾尔", "").replace("壮族", "").replace("回族", "")
            if address.startswith(tmp_name):
                return code, name
        return '', ''

    def get_city_info(self, address: str, province_code: str = ""):
        if province_code:
            code_prefix = province_code[:2]
            for code, name in self.city_dict.items():
                tmp_name = str(name).replace("市", "").replace("自治州", "")
                if tmp_name in address and code.startswith(code_prefix):
                    return code, name
        else:
            for code, name in self.city_dict.items():
                tmp_name = str(name).replace("市", "").replace("自治州", "")
                if tmp_name in address:
                    return code, name
        return '', ''

    def get_district_info(self, address: str, province_code: str):
        code_prefix = province_code[:2]
        for code, name in self.province_dict.items():
            if name in address and code.startswith(code_prefix):
                return code, name
        return '', ''

    @staticmethod
    def replace_folk(town: str):
        folks = ["回族", "满族", "蒙古族", "俄罗斯族", "朝鲜族", "傈僳族", "锡伯族", "达斡尔族", "柯尔克孜族", "鄂伦春族",
                 "畲族", "土家族", "侗族", "瑶族", "苗族", "维吾尔族", "白族", "壮族", "仫佬族", "仡佬族", "彝族", "藏族",
                 "羌族", "傣族", "纳西族", "白族", "水族", "毛南族", "普米族", "哈尼族", "佤族", "拉祜族", "德昂族", "布朗族",
                 "基诺族", "阿昌族", "怒族", "东乡族", "土族", "哈萨克族", "塔吉克族"]
        for folk in folks:
            town = town.replace(folk, "")
        return town

    def get_district_town_village_info(self, district_datas, tmp_split_after_city_address, city_code):
        district_code = ""
        district_name = ""
        town_code = ""
        town_name = ""
        village_code = ""
        village_name = ""
        logger.info(f"递进查询区县、乡镇和村镇信息")
        for k1, v1 in district_datas.items():
            base_district_name = v1["district_name"]
            if not base_district_name:
                continue
            tmp_base_district_name = base_district_name
            if tmp_base_district_name in tmp_split_after_city_address and k1.startswith(city_code[:4]):
                district_code = k1
                district_name = base_district_name
                # 获取区县后部分地址
                tmp_split_after_district_address = tmp_split_after_city_address.split(tmp_base_district_name, 1)[-1]
                town_datas = v1.get("town_datas", {})

                for k2, v2 in town_datas.items():
                    base_town_name = v2["town_name"]
                    tmp_base_town_name = base_town_name
                    tmp_base_town_name = self.replace_folk(tmp_base_town_name)
                    if len(tmp_base_town_name) <= 1:
                        tmp_base_town_name = base_town_name
                    if tmp_base_town_name in tmp_split_after_district_address and k2.startswith(district_code[:6]):
                        town_code = k2
                        town_name = base_town_name
                        # 获取乡镇后部分地址
                        tmp_split_after_town_address = tmp_split_after_district_address.split(tmp_base_town_name, 1)[-1]
                        village_datas = v2.get("village_datas", {})

                        for k3, v3 in village_datas.items():
                            base_village_name = str(3)
                            # 去掉额外字符，提高村镇识别精度
                            tmp_base_village_name = base_village_name \
                                .replace("村村民委员会", "村") \
                                .replace("村民委员会", "村") \
                                .replace("村委会", "村") \
                                .replace("村村民居委会", "村") \
                                .replace("社区居委会", "社区") \
                                .replace("居民委员会", "") \
                                .replace("居委会", "") \
                                .replace("委员会", "") \
                                .replace("委会", "")
                            if len(tmp_base_village_name) <= 1:
                                tmp_base_village_name = base_village_name
                            if tmp_base_village_name in tmp_split_after_town_address and k3.startswith(town_code[:9]):
                                village_code = k3
                                village_name = base_village_name
                                return district_name, district_code, town_name, town_code, village_name, village_code
        return district_name, district_code, town_name, town_code, village_name, village_code

    def handle_address(self, address: str):
        status_code = 1
        info = "success"
        address_code = "000000000000"
        logger.info(f"地址：{address}")
        province_code, province_name = self.get_province_info(address)
        if not province_code:
            logger.info("未查询到省份信息，先略过省份查询，优先查询城市信息")
            city_code, city_name = self.get_city_info(address_code)
            if city_code:
                province_code = city_code[:2] + '0' * 10
                province_name = self.province_dict.get(province_code)
                logger.info(f"优先查询到城市：{city_name}")
            else:
                info = "省份和城市信息均未查到"
                logger.info(info)
                return status_code, info, address_code

        logger.info(f"解析到省份：{province_name}")
        address_code = province_code
        province_datas = self.address_datas.get(province_code, {})
        city_datas = province_datas.get("city_datas", {})
        city_code, city_name = self.get_city_info(address, province_code)
        if not city_code:
            logger.info(f"未查询到城市信息，先略过城市查询，优先查询区县信息")
            district_code, district_name = self.get_district_info(address, province_code)
            if district_code:
                city_code = district_code[:4] + '0' * 8
                city_name = self.city_dict.get(city_code)
                logger.info(f"优先查询到曲线：{district_name}")
            else:
                info = "城市和区县均未查到"
                logger.info(info)
                return status_code, info, address_code

        logger.info(f"解析到城市：{city_name}")
        tmp_city_name = city_name.replace("市", "").replace("自治州", "").replace("地区", "")
        address_code = city_code
        tmp_split_after_city_address = address.split(tmp_city_name, 1)[-1]
        district_datas = city_datas.get(city_code, {}).get("district_datas", {})
        district_name, district_code, town_name, town_code, village_name, village_code = self.get_district_town_village_info(district_datas, tmp_split_after_city_address, city_code)
        logger.info(f"区县：{district_name or None}；乡镇：{town_name or None}；村镇：{village_name or None}")
        # 如果递进未匹配到乡镇，则再正则匹配查找
        if town_name == "":
            logger.info(f"乡镇信息未查到，继续使用正则表达式再次匹配查找")
            address_match_pattern = r"(.*?省|.*?自治区|.*?市)?(.*?市|.*?自治州)?(.*?区|.*?县)?(.*?镇|.*?乡|.*?街道|.*?街|.*?办事处)?(.*?村|.*?社区|.*?路)？"
            search_obj = re.search(address_match_pattern, address)
            if search_obj:
                match_town_name = search_obj.group(4)
                match_village_name = search_obj.group(5)
                if match_town_name and town_name == "":
                    town_name = match_town_name
                if match_village_name and village_name == "":
                    village_name = match_village_name
                logger.info(f"正则匹配到乡镇和村镇信息：{match_town_name}；{match_village_name}")
                # 将正则匹配到的乡镇和村镇与编码表中的数据进行比对查找
                if match_town_name:
                    for code, name in self.town_dict.items():
                        if not name:
                            continue
                        if district_code:
                            if not code[0:6] == district_code[0:6]:
                                continue
                        else:
                            if not code[0:4] == city_code[0:4]:
                                continue
                        tmp_name = name
                        if len(tmp_name) <= 1:
                            tmp_name = name
                        if tmp_name in match_town_name or match_town_name in tmp_name:
                            logger.info(f"正则查找到乡镇信息：{name}")
                            town_code = code
                            town_name = name
                            break
                if match_village_name:
                    for code, name in self.village_dict.items():
                        if not name:
                            continue
                        if town_code:
                            if not code[0:9] == town_code[0:9]:
                                continue
                        elif district_code:
                            if not code[0:6] == district_code[0:6]:
                                continue
                        else:
                            if not code[0:4] == city_code[0:4]:
                                continue
                        tmp_name = name \
                            .replace("村村民委员会", "村") \
                            .replace("村民委员会", "村") \
                            .replace("村委会", "村") \
                            .replace("村村民居委会", "村") \
                            .replace("社区居委会", "社区") \
                            .replace("居民委员会", "") \
                            .replace("居委会", "") \
                            .replace("委员会", "") \
                            .replace("委会", "")
                        if len(tmp_name) <= 1:
                            tmp_name = name
                        if tmp_name in match_village_name or match_village_name in tmp_name:
                            logger.info(f"正则查找到村镇信息：{name}")
                            village_code = code
                            village_name = name
                            break
        # 反向替换
        if not town_code and village_code:
            town_code = village_code[0:9] + "0" * 3
            town_name = self.town_dict.get(town_code)
            logger.info(f"反向查找到乡镇：{town_name}")
        if not district_code and town_code:
            district_code = town_code[0:6] + "0" * 6
            district_name = self.district_dict.get(district_code)
            logger.info(f"反向查找到区县：{district_name}")

        address_code = village_code or town_code or district_code or city_code or address_code
        logger.info(f"{province_name}: {province_code}, {city_name}: {city_code}, {district_name}: {district_code}, {town_name}: {town_code}, {village_name}: {village_code}")
        return status_code, info, address_code


address_handle = AddressHandler()


def process(address):
    logger.info("========== start process ==========")
    output_data = {
        "code": -1,
        "info": "fail",
        "address_code": "",
    }
    try:
        status_code, info, address_code = address_handle.handle_address(address)
        output_data["code"] = status_code
        output_data["info"] = info
        output_data["address_code"] = address_code
    except Exception as e:
        logger.error(traceback.format_exc())
        output_data["code"] = -1
        output_data["info"] = f"fail: {e}"
    finally:
        logger.info(f"output_data: {output_data}")
        logger.info("========== end process ==========")
        return output_data


if __name__ == '__main__':
    address_list = ["四川省遂宁高新区宝升镇插板堰村", "山东省淄博市临淄区齐都镇安合村委会", ""]
    for address in address_list:
        output = process(address)
        print(output)
        # {'code': 1, 'info': 'success', 'address_code': '510900000000'}
        # {'code': 1, 'info': 'success', 'address_code': '370305100000'}
        # {'code': 1, 'info': '省份和城市信息均未查到', 'address_code': '000000000000'}
